/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.action.core.contentFieldValue;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.util.ConvertUtil;
import com.blandware.atleap.model.core.*;
import com.blandware.atleap.service.core.ContentFieldManager;
import com.blandware.atleap.service.core.ContentLocaleManager;
import com.blandware.atleap.service.core.LookupManager;
import com.blandware.atleap.service.core.LayoutManager;
import com.blandware.atleap.webapp.action.core.BaseAction;
import com.blandware.atleap.webapp.form.core.ContentFieldValueForm;
import com.blandware.atleap.webapp.util.core.LocaleUtil;
import com.blandware.atleap.webapp.util.core.WebappConstants;
import com.blandware.atleap.webapp.util.core.WebappUtil;
import com.blandware.atleap.webapp.struts.HeritableComponentDefinition;
import org.apache.commons.validator.GenericValidator;
import org.apache.struts.action.*;
import org.apache.struts.tiles.TilesUtil;
import org.apache.struts.tiles.DefinitionsFactoryException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletContext;
import java.util.*;

/**
 * <p>Prepares form bean to create or update content field value's data
 * </p>
 * <p><a href="CallCreateOrUpdateContentFieldValueAction.java.html"><i>View Source</i></a></p>
 * <p/>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.21 $ $Date: 2007/12/22 10:48:46 $
 * @struts.action path="/core/contentFieldValue/callCreateOrUpdate"
 * name="contentFieldValueForm"
 * scope="request"
 * validate="false"
 * roles="core-contentFieldValue-createOrUpdate, core-contentFieldValue-view"
 * @struts.action-forward name="createOrUpdateContentFieldValue"
 * path=".core.contentFieldValue.createOrUpdate"
 * @struts.action-forward name="listContentFieldValues"
 * path="/core/contentFieldValue/list.do"
 * redirect="false"
 * @struts.action-forward name="listContentFields"
 * path="/core/contentField/list.do"
 * redirect="false"
 */
public final class CallCreateOrUpdateContentFieldValueAction extends BaseAction {
	/**
	 * @param mapping  The ActionMapping used to select this instance
	 * @param form     The optional ActionForm bean for this request (if any)
	 * @param request  The HTTP request we are proceeding
	 * @param response The HTTP response we are creating
	 * @return an ActionForward instance describing where and how
	 *         control should be forwarded, or null if response
	 *         has already been completed
	 */
	public ActionForward execute(ActionMapping mapping, ActionForm form,
	                             HttpServletRequest request, HttpServletResponse response) throws Exception {

        HttpSession session = request.getSession();
        ServletContext servletContext = servlet.getServletContext();

        // Check whether it's normal edit (from list of field values) or
		// it was invoked from context menu (or from 'view linked objects' page)

		String redirectUrl = request.getParameter("redirectUrl");
        if ( !GenericValidator.isBlankOrNull(redirectUrl) ) {
			// It was invoked from context menu
			session.setAttribute(WebappConstants.REDIRECT_URL_KEY, redirectUrl);
		}

		session.removeAttribute(WebappConstants.CONTENT_FIELD_VALUE_SUBMITTED_ACTION_KEY);

		ContentFieldValueForm contentFieldValueForm = (ContentFieldValueForm) form;

        boolean creatingField = !GenericValidator.isBlankOrNull(contentFieldValueForm.getOwnerId())
                && !GenericValidator.isBlankOrNull(contentFieldValueForm.getFieldType());
        boolean creatingIndexedFV = creatingField
                && !GenericValidator.isBlankOrNull(contentFieldValueForm.getIndexedIdentifier());
        
        Long contentFieldId = null;
        Long ownerId = null;
        String fieldIdentifier = null;
        String indexedIdentifier = null;
        byte fieldType = ContentField.LINE_TYPE;
        if (creatingField) {
            if (creatingIndexedFV && "null".equals(contentFieldValueForm.getOwnerId())) {
                // looking for layout only if case of creation of new indexed
                // field index
                ownerId = lookupLayoutId(contentFieldValueForm.getDefinition(),
                                         request, servletContext);
                if (ownerId != null) {
                    contentFieldValueForm.setOwnerId(ownerId.toString());
                }
            } else {
                ownerId = Long.valueOf(contentFieldValueForm.getOwnerId());
            }
            if (creatingIndexedFV) {
                indexedIdentifier = contentFieldValueForm.getIndexedIdentifier();
            } else {
                fieldIdentifier = contentFieldValueForm.getIdentifier();
            }
            fieldType = Byte.valueOf(contentFieldValueForm.getFieldType())
                    .byteValue();
            if (fieldType != ContentField.LINE_TYPE
                    && fieldType != ContentField.MULTILINE_TYPE
                    && fieldType != ContentField.HTML_TYPE) {
                fieldType = ContentField.LINE_TYPE;
            }
            if (ownerId == null) {
                if ( log.isWarnEnabled() ) {
                    log.warn("Missing owner ID. Returning to index...");
                }
                return mapping.findForward("admin");
            }
        } else {
            if ( !GenericValidator.isBlankOrNull(contentFieldValueForm.getContentFieldId()) ) {
                contentFieldId = Long.valueOf(contentFieldValueForm.getContentFieldId());
            } else if ( session.getAttribute(WebappConstants.CONTENT_FIELD_ID_KEY) != null ) {
                contentFieldId = (Long) session.getAttribute(WebappConstants.CONTENT_FIELD_ID_KEY);
            } else {
                if ( log.isWarnEnabled() ) {
                    log.warn("Missing content field ID. Returning to index...");
                }
                return mapping.findForward("admin");
            }
        }

        session.setAttribute(WebappConstants.CONTENT_FIELD_ID_KEY, contentFieldId);

		if ( isCancelled(request) ) {
            // if there's redirect URL in session, return to that URL
            String url = (String) session.getAttribute(WebappConstants.REDIRECT_URL_KEY);
            if (!GenericValidator.isBlankOrNull(url)) {
                session.removeAttribute(WebappConstants.REDIRECT_URL_KEY);
                return new ActionForward(url, true);
            }
			return mapping.findForward("listContentFieldValues");
		}

        if (!request.isUserInRole("core-contentFieldValue-createOrUpdate")) {
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			return null;
        }

        if (creatingField &&
                !request.isUserInRole("core-contentField-create")) {
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			return null;
        }

        if (creatingIndexedFV &&
                !request.isUserInRole("core-contentField-createIndexed")) {
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			return null;
        }

        // remove attribute 'inputId' from session, if it exists
		session.removeAttribute(WebappConstants.HTML_INPUT_TAG_ID_KEY);

		ContentFieldManager contentFieldManager = (ContentFieldManager) getBean(Constants.CONTENT_FIELD_MANAGER_BEAN);
		ContentLocaleManager contentLocaleManager = (ContentLocaleManager) getBean(Constants.CONTENT_LOCALE_MANAGER_BEAN);

		List cfvLocales = null;

		if ( request.getAttribute(WebappConstants.CONTENT_FIELD_VALUE_SELECTED_LOCALES_KEY) != null ) {
			cfvLocales = (List) request.getAttribute(WebappConstants.CONTENT_FIELD_VALUE_SELECTED_LOCALES_KEY);
		} else if ( !GenericValidator.isBlankOrNull(request.getParameter("locale")) ) {
			String locale = request.getParameter("locale");
			cfvLocales = new ArrayList();
			cfvLocales.add(locale);
		}
        
        ContentField contentField;
        Localizable owner = null;

        if (!creatingField) {
            // retrieve requested content field
            contentField = contentFieldManager.retrieveContentField(contentFieldId);

            if ( contentField == null ) {
                // content field not found. it might has already been deleted
                ActionMessages errors = new ActionMessages();
                errors.add("contentFieldNotFound", new ActionMessage("core.contentField.errors.notFound"));
                saveErrors(request, errors);
                return mapping.findForward("listContentFields");
            }

            // export field into session
            session.setAttribute(WebappConstants.CONTENT_FIELD_KEY, contentField);
        } else {
            // checking for owner existence
            LookupManager lookupManager = (LookupManager) getBean(Constants.LOOKUP_MANAGER_BEAN);
            owner = lookupManager.retrieveLocalizable(ownerId);
            if ( owner == null ) {
                // owner not found. it might has already been deleted
                ActionMessages errors = new ActionMessages();
                errors.add("ownerNotFound", new ActionMessage("core.contentField.errors.ownerNotFound"));
                saveErrors(request, errors);
                return mapping.findForward("admin");
            }

            // just for convinience - empty field
            contentField = new ContentField();

            session.removeAttribute(WebappConstants.CONTENT_FIELD_KEY);
        }

        SortedMap contentFieldValues = new TreeMap();
		boolean CFVLocalesSpecified = cfvLocales != null && cfvLocales.size() > 0;

		if ( !CFVLocalesSpecified ) {
            List enabledLocales = contentLocaleManager.getEnabledLocales();
            Map contentFieldValuesMap = contentField.getContentFieldValuesMap();
            for (Iterator i = enabledLocales.iterator(); i.hasNext();) {
                ContentLocale contentLocale = (ContentLocale) i.next();
                ContentFieldValue contentFieldValue = (ContentFieldValue) contentFieldValuesMap.get(contentLocale.getIdentifier());
                contentFieldValues.put(contentLocale, contentFieldValue);
            }
		} else {
			for ( int i = 0; i < cfvLocales.size(); i++ ) {
				String localeIdentifier = (String) cfvLocales.get(i);
				ContentLocale locale = contentLocaleManager.retrieveContentLocale(localeIdentifier);
				if ( locale != null ) {
					ContentFieldValue contentFieldValue = null;
                    if (!creatingField) {
                        contentFieldValue = contentFieldManager.findContentFieldValueByContentFieldIdAndLocale(contentFieldId, localeIdentifier);
                    }
                    contentFieldValues.put(locale, contentFieldValue);
				}
			}
		}

		if (contentFieldValues.isEmpty()) {
			// content field value not found. it might be deleted by someone else
			ActionMessages errors = new ActionMessages();
			errors.add("contentFieldValueNotFound", new ActionMessage("core.contentFieldValue.errors.nothingFound"));
			saveErrors(request, errors);
			return mapping.findForward("listContentFieldValues");
		}

		Map mergedMap = null;
		if ( request.getAttribute(WebappConstants.CONTENT_FIELD_VALUES_MERGED_KEY) != null ) {
			mergedMap = (Map) request.getAttribute(WebappConstants.CONTENT_FIELD_VALUES_MERGED_KEY);
		}

		for ( Iterator i = contentFieldValues.entrySet().iterator(); i.hasNext(); ) {
			Map.Entry entry = (Map.Entry) i.next();
			ContentLocale locale = (ContentLocale) entry.getKey();
			ContentFieldValue contentFieldValue = (ContentFieldValue) entry.getValue();
			String key = locale.getIdentifier();

			contentFieldValueForm.getLocaleMap().put(key, locale);
			if ( contentFieldValue != null ) {
				contentFieldValueForm.setId(key, contentFieldValue.getId());
				if ( mergedMap == null ) {
					String newValue;
					if ( contentFieldValue.getContentField().getType() == ContentField.LINE_TYPE ) {
						newValue = contentFieldValue.getSimpleValue();
					} else {
						newValue = ConvertUtil.convertToString(contentFieldValue.getValue());
					}
					contentFieldValueForm.setValue(key, newValue);
				} else {
					contentFieldValueForm.setValue(key, mergedMap.get(contentFieldValue.getContentLocale().getIdentifier()));
				}
				contentFieldValueForm.setVersion(key, contentFieldValue.getVersion());
			} else {
				contentFieldValueForm.setId(key, new Long(-1));
				contentFieldValueForm.setValue(key, "");
				contentFieldValueForm.setVersion(key, new Integer(0));
			}
		}

		// add empty values for locales, for which content has not been created yet
		// this step is performed if and only if no CFV locale has been provided
		if ( !CFVLocalesSpecified ) {
            List contentLocales = new ArrayList(LocaleUtil.getInstance(servletContext).getEnabledLocales());
			for ( Iterator i = contentLocales.iterator(); i.hasNext(); ) {
				ContentLocale locale = (ContentLocale) i.next();
				String identifier = locale.getIdentifier();
				if ( !contentFieldValueForm.getIdMap().containsKey(identifier) ) {
					contentFieldValueForm.setId(identifier, new Long(-1));
					contentFieldValueForm.setValue(identifier, "");
					contentFieldValueForm.setVersion(identifier, new Integer(0));
					// TODO: fix it
					// methods getLocale and setLocale are not available in form, because there is 'locale' parameter
					// in request in some actions, so use java.util.Map#put
					contentFieldValueForm.getLocaleMap().put(identifier, locale);
				}
			}
		}

		// get information about owner
        if (!creatingField) {
            owner = contentField.getOwner();
        }
        String ownerInfo = WebappUtil.getLocalizableInfo(owner, request);
        session.setAttribute(WebappConstants.OWNER_INFO_KEY, ownerInfo);

		return mapping.findForward("createOrUpdateContentFieldValue");
	}

    /**
     * Returns ID of layout which has specified definition.
     *
     * @param definition definition by which to search
     * @param request request
     * @param servletContext servlet context
     * @return ID or null if not found
     */
    protected Long lookupLayoutId(String definition, HttpServletRequest request,
                                  ServletContext servletContext) {
        LayoutManager layoutManager
                = (LayoutManager) getBean(Constants.LAYOUT_MANAGER_BEAN);
        Layout layout;
        do {
            layout = layoutManager.findLayoutByDefinition(definition);
            if ( layout != null ) {
                break;
            }
            try {
                definition = ((HeritableComponentDefinition) TilesUtil.getDefinition(definition, request, servletContext)).getExtends();
            } catch (DefinitionsFactoryException e) {
                break;
            }
        } while ( definition != null );
        return layout == null ? null : layout.getId();
    }
}